from parser import Parser
from re import compile

pattern = compile(r'\b(def \w+\(.*\):)|(class \w+(\(.*\))?:)')

class StatementParser(Parser):
    
    #everything inside the following tags is ignored
    tags = (('<string>','</string>'),)
    
    def parse(self, text):
        text = self.normalize(text)
        statements = self.splitStatements(text)
        return self.splitAssignments(statements)
    
    def splitStatements(self, text):
        '''
        Splits the text up into statements and returns them as list.
        '''
        
        self._updateTagsList(text)
        self.pos = 0
        buf = []
        def end_of_statement(eos):
            if not self._insideTag(eos.start()):
                statement = text[self.pos:eos.start()]
                if statement:
                    buf.append(statement)
                self.pos = eos.end()
        self.settings.pattern['eos'].sub(end_of_statement, text)
        
        statement = text[self.pos:].rstrip(' \t')
        if statement:
            buf.append(statement)
        
        return buf
    
    def splitAssignments(self, statements):
        '''
        Splits each statement in the statements list up into
        the part left and the part right of the equal sign.
        returns a list of tuples:
        [(l1,r1), (l2, r2), ...]
        '''
        
        statementsBuffer = []
        for s in statements:
            self._updateTagsList(s)
            buf = None               
            for i in self.settings.pattern['assignment'].finditer(s):
                if not self._insideTag(i.start()):
                    buf = [s[:i.start()], s[i.end():]]
                    break
            if not buf:
                buf = ['', s]
            statementsBuffer.append(buf)
        return statementsBuffer
    
    def normalize(self, text):
        '''
        First, all comments are erased. Afterwards all multiple line statements
        and also lists, tuples and dictionaries are converted to one-liners.
        '''
        
        text = self._clearComments(text) 
        text = self.settings.pattern['multiline'].sub('', text)
        posList = self._getOutestBrackets(text)
        def subst(word):
            if self._insideTag(word.start()):
                return word.group()
        self._updateTagsList(text)
        buf = []
        lastEnd = 0
        for start, end in posList:
            buf.append(text[lastEnd:start])
            l = self.settings.pattern['eol'].sub(subst, text[start:end])
            buf.append(l)
            lastEnd = end
        buf.append(text[lastEnd:])
            
        return ''.join(buf)
    
    def _clearComments(self, text):
        '''
        Returns the text after deleting all comments.
        '''
        
        self._updateTagsList(text)
        end = 0
        buf = []
        while True:
            start = text.find('#', end)
            
            if start==-1:
                buf.append(text[end:])
                break
            
            
            if self._insideTag(start):
                buf.append(text[end:start+1])
                end = start+1
                continue

            buf.append(text[:start])
            
            end = text.find('\n', start)
            if end==-1:
                break
            
        return ''.join(buf)
        
    def _getOutestBrackets(self, text):
        '''
        Returns a list with only the outest brackets as tuples.
        '''
        def sort(a, b):
            if a[0]>b[0]:
                return 1
            return -1
        posList = self._getBrackets(text)
        posList.sort(sort)
        pos = -1
        buf = []
        for p in posList:
            if p[0]>pos:
                buf.append(p)
                pos = p[1]
        return buf
    
    def _getBrackets(self, text):
        '''
        Returns a list of the positions of all encapsulating brackets as tuples:
        [(p1, p2), (p1, p2), ...]
        '''
        
        l = len(text)
        self._updateTagsList(text)
        posList = []
        for i in ('(','[','{'):
            p2 = 0
            while True:
                p1 = text.find(i, p2)
                if p1==-1:
                    break
                if self._insideTag(p1):
                    p2 = p1+1
                    continue
                p2 = self._findEndBracket(text, p1)
                if p2==l:
                    continue
                posList.append((p1,p2))
        return posList
    
    def _findEndBracket(self, text, start):
        '''
        Returns the position of the closing bracket, given the position
        of a starting bracket.
        '''
        
        bracket = {'(':')',
                   '{':'}',
                   '[':']'}
        
        b1, b2 = text[start], bracket[text[start]]
        
        rank = 0
        for char in text[start:]:
            if self._insideTag(start):
                start+=1
                continue
            if char==b1:
                rank+=1
            elif char==b2:
                rank-=1
            if rank==0:
                break
            start+=1
        return start